package com.qttaudio.example.utils;

import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;

import com.qttaudio.sdk.channel.ChannelEngine;
import com.qttaudio.sdk.channel.ChannelFactory;
import com.qttaudio.sdk.channel.ChannelPlayer;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Mp3Player主要演示使用AudioSource来播放音效、混音等用法
 * SDK使用接口地方都有注释，请重点关注
 */
public class Mp3Player {
    private boolean mIsStarted = false;
    private boolean mIsPause = false;
    private String mPlayFile;
    private ChannelEngine channelEngine = null;
    private ChannelPlayer mLocalAudioSource = null;
    private ChannelPlayer mRemoteAudioSource = null;
    private boolean localPlay = true;
    private boolean remotePlay = false;
    private MediaExtractor mediaExtractor = null;
    private MediaCodec codec = null;
    private Thread mPlayThread = null;

    private Object pauseFlag = new Object();

    public void setFile(String filename) {
        mPlayFile = filename;
    }

    public Mp3Player() {
        channelEngine = ChannelFactory.GetChannelInstance();
    }

    public int start() {
        if (mIsStarted) {
            if (mIsPause) {
                mIsPause = false;
                synchronized (pauseFlag) {
                    pauseFlag.notify();
                }
            }
            return 0;
        }
        int samplerate = -1;
        int channel = -1;
        mediaExtractor = new MediaExtractor();
        try {
            mediaExtractor.setDataSource(mPlayFile);
            for (int i = 0; i < mediaExtractor.getTrackCount(); i++) {
                MediaFormat format = mediaExtractor.getTrackFormat(i);
                String mime = format.getString(MediaFormat.KEY_MIME);
                if (mime.startsWith("audio")) {
                    mediaExtractor.selectTrack(i);
                    codec = MediaCodec.createDecoderByType(mime);
                    codec.configure(format, null, null, 0);
                    samplerate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
                    channel = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            mediaExtractor.release();
            return -1;
        }
        if (codec == null) {
            mediaExtractor.release();
            return -1;
        }
        if (samplerate == -1 || channel == -1) {
            codec.flush();
            codec.release();
            mediaExtractor.release();
            return -1;
        }
        codec.start();

        /**
         * AudioSource播放的效果在本地还是对端，由AudioEngine的AudioStream方向决定
         * OutputStream播放效果在本地
         * InputStream播放效果在对端
         * AudioSource可以创建多个，但不可多于20个；最终播放效果是AudioSource之间混音的效果
         * 开发者可创建两个以上本地的AudioSource对象，分别播放的不同的声源，播放出来是所有声源同时放的一个混音效果
         */

        //创建一个本地播放的AudioSource对象，接收PCM原始数据输入。放出来的声音自己本地可以听到，对方听不到。
        mLocalAudioSource = channelEngine.createLocalPlayer(samplerate, channel);
        //本地声音一般比较大，这里将音量调小一点，60%音量
        mLocalAudioSource.adjustVolume(0.6f);

        //创建一个远程播放的AudioSource对象，接收PCM原始数据输入。放出来的声音对方可以听到，自己本地听不到。
        mRemoteAudioSource = channelEngine.createRemotePlayer(samplerate, channel);

        mIsStarted = true;
        mPlayThread = new PlayThread();
        mPlayThread.start();

        return 0;
    }

    public void pause() {
        if (!mIsStarted)
            return;
        mIsPause = true;
    }

    public void stop() {
        if (!mIsStarted)
            return;
        mIsStarted = false;
        mIsPause = false;
        synchronized (pauseFlag) {
            pauseFlag.notify();
        }
        try {
            mPlayThread.join();
            mPlayThread = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void enableLocalPlay(boolean val) {
        localPlay = val;
    }

    public void enableRemotePlay(boolean val) {
        remotePlay = val;
    }

    private class PlayThread extends Thread {
        @Override
        public void run() {
            while (mIsStarted) {
                if (mIsPause) {
                    synchronized (pauseFlag) {
                        try {
                            pauseFlag.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                if (!mIsStarted)
                    break;
                ByteBuffer[] inputBuffers = codec.getInputBuffers();
                ByteBuffer[] outputBuffers = codec.getOutputBuffers();
                MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                int inputIndex = codec.dequeueInputBuffer(10000);
                if (inputIndex < 0) {
                    break;
                }
                ByteBuffer inputBuffer = inputBuffers[inputIndex];
                inputBuffer.clear();
                int sampleSize = mediaExtractor.readSampleData(inputBuffer, 0);
                if (sampleSize < 0) {
                    codec.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
                    mediaExtractor.advance();
                } else {
                    codec.queueInputBuffer(inputIndex, 0, sampleSize, 0, 0);
                    mediaExtractor.advance();
                }

                int outputIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);

                ByteBuffer outputBuffer;
                byte[] chunkPCM;
                while (outputIndex >= 0) {//每次解码完成的数据不一定能一次吐出 所以用while循环，保证解码器吐出所有数据
                    outputBuffer = outputBuffers[outputIndex];//拿到用于存放PCM数据的Buffer
                    int size = bufferInfo.size;
                    if (size > 0) {
                        chunkPCM = new byte[size];//BufferInfo内定义了此数据块的大小
                        outputBuffer.get(chunkPCM);//将Buffer内的数据取出到字节数组中
                        outputBuffer.clear();//数据取出后一定记得清空此Buffer MediaCodec是循环使用这些Buffer的，不清空下次会得到同样的数据
//                        Log.d("qtt_debug", "mLocalAudioSource write="+chunkPCM.length);

                        /**
                         * localPlay和remotePlay由界面两个CheckBox控制
                         * 将mp3解码的pcm数据，写入AudioSource对象，本地或者对端，就会出播放声音
                         */
                        if (localPlay)
                            mLocalAudioSource.write(chunkPCM, 0, chunkPCM.length);
                        if (remotePlay)
                            mRemoteAudioSource.write(chunkPCM, 0, chunkPCM.length);
                    }
                    codec.releaseOutputBuffer(outputIndex, false);//此操作一定要做，不然MediaCodec用完所有的Buffer后 将不能向外输出数据
                    outputIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);//再次获取数据，如果没有数据输出则outputIndex=-1 循环结束
                }
            }
            mIsStarted = false;
            codec.flush();
            codec.release();
            mediaExtractor.release();
            /**
             * 歌曲播放完了，AudioSource需要stop（start的逆向操作），需要delete（createFileSource的逆向操作）
             */
            mLocalAudioSource.delete();
            mRemoteAudioSource.delete();
        }
    }
}
